import { deepMix } from '@antv/util';
import { Params } from '../../../../src/core/adaptor';
import { WordCloudOptions } from '../../../../src/plots/word-cloud';
import {
  getFontSizeMapping,
  getSingleKeyValues,
  getSize,
  processImageMask,
  transform,
} from '../../../../src/plots/word-cloud/utils';
import { CountryEconomy } from '../../../data/country-economy';
import { createDiv } from '../../../utils/dom';

describe('word-cloud utils', () => {
  const params: Params<WordCloudOptions> = {
    chart: {
      width: 500,
      height: 500,
      padding: 10,
      appendPadding: 10,
    } as any,
    options: {
      data: CountryEconomy,
      wordField: 'Country',
      weightField: 'GDP',
      autoFit: false,
      timeInterval: 2000,
      imageMask: null,
      wordStyle: {
        fontFamily: 'Verdana',
        fontWeight: 'normal',
        padding: 1,
        fontSize: [20, 60],
        rotation: [0, 90],
        rotationSteps: 2,
      },
    },
  };

  it('utils: transform', () => {
    const result = transform(params).filter((v) => v.hasText);

    expect(CountryEconomy.some((v) => v.Country === result[0].text)).toBe(true);
    expect(CountryEconomy.some((v) => v.GDP === result[0].value)).toBe(true);
    expect(result[0].font).toBe('Verdana');
    expect(result[0].style).toBe('normal');
    expect(result[0].weight).toBe('normal');
    expect(result.every((row) => row.size >= 20 && row.size <= 60)).toBe(true);
    expect(result.every((row) => row.rotate === 0 || row.rotate === 90)).toBe(true);
  });

  it('utils: transform, no data', () => {
    const p = deepMix({}, params, {
      options: {
        data: null,
      },
    });
    expect(transform(p).length).toBe(0);
  });

  it('utils: transform, data is empty', () => {
    const p = deepMix({}, params, {
      options: {
        data: [],
      },
    });
    expect(transform(p).length).toBe(0);
  });

  it('utils: getSize & autoFit is true', () => {
    const container = createDiv();
    container.setAttribute('style', 'display: inline-block; width: 100px; height: 100px');

    const size = getSize({
      autoFit: true,
      width: 200,
      height: 200,
      padding: 0,
      appendPadding: 0,
      container,
    });

    expect(size).toEqual([100, 100]);
  });

  it('utils: getSize & autoFit is false', () => {
    const container = createDiv();
    container.setAttribute('style', 'display: inline-block; width: 100px; height: 100px');

    const size = getSize({
      autoFit: false,
      width: 200,
      height: 200,
      padding: 0,
      appendPadding: 0,
      container,
    });

    expect(size).toEqual([200, 200]);
  });

  it('utils: getSize & width and height both 0', () => {
    const container = createDiv();
    container.setAttribute('style', 'display: inline-block; width: 100px; height: 100px');

    const size = getSize({
      autoFit: false,
      width: 0,
      height: 0,
      padding: 0,
      appendPadding: 0,
      container,
    });

    expect(size).toEqual([400, 400]);
  });

  it('utils: getSize & padding and appendPadding', () => {
    const container = createDiv();
    container.setAttribute('style', 'display: inline-block; width: 100px; height: 100px');

    const size = getSize({
      autoFit: false,
      width: 200,
      height: 200,
      padding: 10,
      appendPadding: 10,
      container,
    });

    expect(size).toEqual([160, 160]);
  });

  it('utils: padding is Array', () => {
    const p1 = deepMix({}, params, {
      chart: {
        padding: [10],
      },
    });
    const p2 = deepMix({}, params, {
      chart: {
        padding: [10, 10],
      },
    });
    const p3 = deepMix({}, params, {
      chart: {
        padding: [10, 10, 10],
      },
    });
    const p4 = deepMix({}, params, {
      chart: {
        padding: [10, 10, 10, 10],
      },
    });
    const p5 = deepMix({}, params, {
      chart: {
        padding: [],
      },
    });

    const r1 = transform(p1).filter((v) => v.hasText);
    const r2 = transform(p2).filter((v) => v.hasText);
    const r3 = transform(p3).filter((v) => v.hasText);
    const r4 = transform(p4).filter((v) => v.hasText);
    const r5 = transform(p5).filter((v) => v.hasText);

    expect(r1.length > 0).toBe(true);
    expect(r2.length > 0).toBe(true);
    expect(r3.length > 0).toBe(true);
    expect(r4.length > 0).toBe(true);
    expect(r5.length > 0).toBe(true);
  });

  it('utils: padding is String', () => {
    const p1 = deepMix({}, params, {
      chart: {
        padding: 'auto',
      },
    });
    expect(transform(p1).length > 0).toBe(true);
  });

  it('utils: fontSize is a function', () => {
    const p1 = deepMix({}, params, {
      options: {
        wordStyle: {
          fontSize: () => 20,
        },
      },
    });

    const result = transform(p1).filter((v) => v.hasText);
    expect(result.every((row) => row.size === 20)).toBe(true);
  });

  it('utils: fontSize is a number', () => {
    const p1 = deepMix({}, params, {
      options: {
        wordStyle: {
          fontSize: 20,
        },
      },
    });

    const result = transform(p1).filter((v) => v.hasText);
    expect(result.every((row) => row.size === 20)).toBe(true);
  });

  it('utils: rotation is number', () => {
    const p1 = deepMix({}, params, {
      options: {
        wordStyle: {
          rotation: 10,
        },
      },
    });

    const result = transform(p1).filter((v) => v.hasText);
    expect(result.every((row) => row.rotate === 10)).toBe(true);
  });

  it('utils: rotation is function', () => {
    const p1 = deepMix({}, params, {
      options: {
        wordStyle: {
          rotation: () => 10,
        },
      },
    });

    const result = transform(p1).filter((v) => v.hasText);
    expect(result.every((row) => row.rotate === 10)).toBe(true);
  });

  it('utils: rotation is array', () => {
    const p1 = deepMix({}, params, {
      options: {
        wordStyle: {
          rotation: [10, 10],
        },
      },
    });

    const result = transform(p1).filter((v) => v.hasText);
    expect(result.every((row) => row.rotate === 10)).toBe(true);
  });

  it('utils: rotationSteps is 0', () => {
    const p1 = deepMix({}, params, {
      options: {
        wordStyle: {
          rotationSteps: 0, // 等于 0 时，自动转换成默认值 1
        },
      },
    });

    const result = transform(p1).filter((v) => v.hasText);
    expect(result.every((row) => row.rotate === 0)).toBe(true);
  });

  it('utils: processImageMask, HTMLImageElement', async () => {
    const image = new Image();

    const img = await processImageMask(image);
    expect(img).toBe(image);
  });

  it('utils: processImageMask with url, error', async () => {
    // 无效 url
    const url = 'something';
    let img;

    try {
      img = await processImageMask(url);
      expect(img).toBe(undefined);
    } catch (img) {
      expect(img).toBe(undefined);
    }
  });

  it('utils: processImageMask with invalid value', async () => {
    // 无效值
    const url = {} as any;
    let img;

    try {
      img = await processImageMask(url);
      expect(img).toBe(undefined);
    } catch (img) {
      expect(img).toBe(undefined);
    }
  });

  it('utils: processImageMask, base64', async () => {
    const base64 =
      '';

    const img = await processImageMask(base64);
    expect(img instanceof HTMLImageElement).toBe(true);
  });

  it('option: placementStrategy', () => {
    const p = deepMix({}, params, {
      options: {
        placementStrategy: () => ({ x: 100, y: 100 }),
        wordStyle: {
          fontFamily: '字体',
          fontWeight: '字重',
          fontSize: 66,
          rotation: 99,
        },
      },
    });

    const result = transform(p).filter((v) => v.hasText);
    expect(result.length).toBe(CountryEconomy.length);
    result.forEach((item) => {
      expect(item.font).toBe('字体');
      expect(item.weight).toBe('字重');
      expect(item.size).toBe(66);
      expect(item.rotate).toBe(99);
      expect(item.x).toBe(100);
      expect(item.y).toBe(100);
    });
  });

  it('getFontSizeMapping', () => {
    const f1: any = getFontSizeMapping(10);
    expect(f1()).toBe(10);

    const foo = () => 10;
    const f2 = getFontSizeMapping(foo);
    expect(f2).toBe(foo);

    const data = [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, { value: 5 }];
    // 数据中字体权重都相同时，返回字体大小的中间值
    const f3: any = getFontSizeMapping([10, 20], [10, 10]);
    data.forEach((v) => {
      expect(f3(v)).toBe(15);
    });

    // 没有提供字体权重范围的话，返回字体大小的中间值
    const f4: any = getFontSizeMapping([10, 20]);
    data.forEach((v) => {
      expect(f4(v)).toBe(15);
    });

    const f5: any = getFontSizeMapping([10, 20], [1, 5]);
    data.forEach((v) => {
      expect(f5(v) >= 10 && f5(v) <= 20).toBe(true);
    });
  });

  it('getSingleKeyValues', () => {
    const data = [
      { value: 0 },
      { value: 1 },
      { value: '4' },
      { value: null },
      { value: undefined },
      { value: '' },
      { value: NaN },
      { value: false },
      { value: true },
      { value: Symbol('') },
      { value: {} },
      { value: () => 10 },
    ];

    // 只保留数字
    expect(getSingleKeyValues(data, 'value')).toEqual([0, 1]);
  });
});
